import { useAddonSetting } from "@sker/runtime";
import { join } from "path";
import { Digdit, Jsonb, ROOT, unique, useEntityManager } from "@sker/core";
import { DependencyContainer } from "tsyringe";
import { exists, readFile } from 'fs-extra'
import { URL } from "url";
import { SysAddon } from '@sker/entities'
import { DataSource, Entity, PrimaryGeneratedColumn } from "typeorm";

export interface IMetadataSourceTableRelationship {
    definition: any,
    name: string;
}
export interface IMetadataSourceTable {
    table: {
        name: string;
        schema: string;
    },
    remote_relationships: IMetadataSourceTableRelationship[]
}
export interface IMetadataSource {
    name: string;
    kind: string;
    tables: any[];
    configuration: any;
}
export interface IMetadataRemoteDefinition {
    url: string;
    timeout_seconds: number;
    customization: any;
    forward_client_headers: boolean;
}
export interface IMetadataRemote {
    name: string;
    definition: IMetadataRemoteDefinition;
    comment: string;
}

export interface IMetadata {
    version: number;
    sources: IMetadataSource[];
    remote_schemas: IMetadataRemote[];
    actions: any[];
    custom_types: any;
}
export interface MetadataObject {
    resource_version: number;
    metadata: IMetadata;
}

@Entity({ name: 'hdb_metadata' })
class HdbMetadata {
    @PrimaryGeneratedColumn()
    id: number;

    @Jsonb()
    metadata: any;

    @Digdit()
    resource_version: number;
}
const createDataSource = async (url: URL) => {
    const cfg: any = {
        type: 'postgres',
        pool: {
            max: 100,
            min: 1
        },
        username: url.username,
        password: url.password,
        host: url.hostname,
        port: parseInt(url.port),
        database: url.pathname.replace('/', ''),
        entities: [
            HdbMetadata
        ],
        logging: ['error'],
        logger: 'advanced-console',
        loggerLevel: 'info',
        schema: 'hdb_catalog',
        extra: {
            connectionLimit: 10
        }
    }
    const ds = new DataSource(cfg);
    await ds.initialize();
    return ds;
}
export default async (injector: DependencyContainer) => {
    const root = injector.resolve(ROOT)
    const setting = await useAddonSetting('@imeepos/hasura')
    const ormConfigFile = join(root, 'data/plugins/@sker/orm.json')
    const ormConfigStr = (await readFile(ormConfigFile)).toString('utf-8')
    const ormConfig = JSON.parse(ormConfigStr)
    const databaseUrl = new URL(ormConfig.url);
    // get tables
    const tables = await useEntityManager(async m => {
        const list = await m.find(SysAddon)
        return await Promise.all(
            list.map(async addon => {
                const metadataFile = join(root, 'addons', addon.name, 'metadata.json')
                if (await exists(metadataFile)) {
                    const metadataStr = await readFile(metadataFile).then(res => res.toString('utf-8'))
                    return JSON.parse(metadataStr)
                }
                return []
            })
        )
    })
    const uniqueTable = unique(tables.flat(), it => it.table.name)
    const metadataSource: IMetadataSource = {
        name: setting.SOURCE_NAME,
        kind: 'postgres',
        tables: [
            ...uniqueTable
        ],
        configuration: {
            "connection_info": {
                "database_url": `postgres://${databaseUrl.username}:${databaseUrl.password}@${databaseUrl.hostname}:${databaseUrl.port}${databaseUrl.pathname}`,
                "isolation_level": "read-committed",
                "use_prepared_statements": false
            }
        }
    }
    const remote: IMetadataRemote = {
        "name": "wback",
        "definition": {
            "url": `http://${setting.REMOTE_URL}/graphql`,
            "timeout_seconds": 60,
            "customization": {
                "root_fields_namespace": "api",
                "type_names": {
                    "mapping": {}
                }
            },
            "forward_client_headers": true
        },
        "comment": ""
    }
    const metadata: MetadataObject = {
        resource_version: 1,
        metadata: {
            version: 3,
            sources: [
                metadataSource
            ],
            remote_schemas: [
                remote
            ],
            actions: [],
            custom_types: {
                objects: [],
                scalars: [],
                enums: []
            }
        }
    }
    const hasuraDatabaseUrl = new URL(setting.HASURA_GRAPHQL_DATABASE_URL)
    const ds = await createDataSource(hasuraDatabaseUrl)
    await ds.transaction(async m => {
        const list = await m.find(HdbMetadata, {})
        if (list.length > 0) {
            const item = list[0]
            const metadatas = item.metadata;
            const metadataList = metadatas.sources.filter((it: any) => it.name !== metadataSource.name)
            await m.update(HdbMetadata, item.id, {
                metadata: {
                    version: 3,
                    sources: [
                        ...metadataList,
                        metadataSource
                    ],
                    remote_schemas: [
                        remote
                    ] 
                } as any
            })
        }
    })
    await ds.destroy();
    return metadata;
}
